﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;

namespace FastUtil.Data
{
   public class EncryptUtil
    {
        #region CA(RSA)加密
        /// <summary>
        /// 20170812
        /// ypeng
        /// CA公钥加密
        /// </summary>
        /// <param name="Data">待加密数据</param>
        /// <param name="PublicKeyFilePath">公钥（.cer）文件的绝对路径</param>
        /// <returns></returns>
        public static string CAEncrypt(string Data, string PublicKeyFilePath)
        {
            //加密
            String password = "111111";
            string strdata = Data;
            byte[] data = Encoding.GetEncoding("UTF-8").GetBytes(strdata);
            X509Certificate2 objx5092;
            if (string.IsNullOrEmpty(password))
                objx5092 = new X509Certificate2(PublicKeyFilePath);
            else
                objx5092 = new X509Certificate2(PublicKeyFilePath, password);
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(objx5092.PublicKey.Key.ToXmlString(false));  //获取公钥

            //加密块最大长度限制，如果加密数据的长度超过 秘钥长度/8-11，会引发长度不正确的异常，所以进行数据的分块加密
            int MaxBlockSize = rsa.KeySize / 8 - 11;
            //正常长度
            if (data.Length <= MaxBlockSize)
            {
                byte[] hashvalueEcy = rsa.Encrypt(data, false); //加密
                return System.Convert.ToBase64String(hashvalueEcy);
            }
            //长度超过正常值
            else
            {
                using (MemoryStream PlaiStream = new MemoryStream(data))
                using (MemoryStream CrypStream = new MemoryStream())
                {
                    Byte[] Buffer = new Byte[MaxBlockSize];
                    int BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize);
                    while (BlockSize > 0)
                    {
                        Byte[] ToEncrypt = new Byte[BlockSize];
                        Array.Copy(Buffer, 0, ToEncrypt, 0, BlockSize);

                        Byte[] Cryptograph = rsa.Encrypt(ToEncrypt, false);
                        CrypStream.Write(Cryptograph, 0, Cryptograph.Length);
                        BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize);
                    }
                    return System.Convert.ToBase64String(CrypStream.ToArray(), Base64FormattingOptions.None);
                }
            }
        }

        /// <summary>
        /// 20170812
        /// ypeng
        /// CA私钥解密
        /// </summary>
        /// <param name="Cryptograph">密文</param>
        /// <param name="PrivateKeyFilePath">私钥（.pfx）文件的绝对路径</param>
        /// <param name="PassWord">私钥的密码</param>
        /// <returns></returns>
        public static string CADecrypt(string Cryptograph, string PrivateKeyFilePath, string PassWord)
        {
            X509Certificate2 objx5092;
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            if (string.IsNullOrEmpty(PassWord))
                objx5092 = new X509Certificate2(PrivateKeyFilePath);
            else
                objx5092 = new X509Certificate2(PrivateKeyFilePath, PassWord);
            rsa = objx5092.PrivateKey as RSACryptoServiceProvider;

            int MaxBlockSize = rsa.KeySize / 8;    //解密块最大长度限制
            //正常解密
            if (Cryptograph.Length <= MaxBlockSize)
            {
                byte[] hashvalueDcy = rsa.Decrypt(System.Convert.FromBase64String(Cryptograph), false);//解密
                return Encoding.GetEncoding("UTF-8").GetString(hashvalueDcy);
            }
            //分段解密
            else
            {
                using (MemoryStream CrypStream = new MemoryStream(System.Convert.FromBase64String(Cryptograph)))
                using (MemoryStream PlaiStream = new MemoryStream())
                {
                    Byte[] Buffer = new Byte[MaxBlockSize];
                    int BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize);

                    while (BlockSize > 0)
                    {
                        Byte[] ToDecrypt = new Byte[BlockSize];
                        Array.Copy(Buffer, 0, ToDecrypt, 0, BlockSize);

                        Byte[] Plaintext = rsa.Decrypt(ToDecrypt, false);
                        PlaiStream.Write(Plaintext, 0, Plaintext.Length);
                        BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize);
                    }

                    string output = Encoding.GetEncoding("UTF-8").GetString(PlaiStream.ToArray());
                    return output;
                    //List<BaseInfo> baseinfo = JsonConvert.DeserializeObject<List<BaseInfo>>(output);
                    //image1.Src = "data:image/png;base64," + baseinfo[0].Picture;
                }
            }
        }

        /// <summary>
        /// CA私钥签名
        /// </summary>
        /// <param name="WaitHandleContext">原文</param>
        /// <param name="PassWord">密码</param>
        /// <param name="PrivateKeyFilePathstring">私钥</param>
        /// <returns>签名</returns>
        public static string CASignatureFormatter(string PrivateKeyFilePath, string PassWord, string WaitHandleContext)
        {
            X509Certificate2 objx5092;
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            if (string.IsNullOrEmpty(PassWord))
                objx5092 = new X509Certificate2(PrivateKeyFilePath);
            else
                objx5092 = new X509Certificate2(PrivateKeyFilePath, PassWord);
            rsa = objx5092.PrivateKey as RSACryptoServiceProvider;
            UnicodeEncoding ByteConverter = new UnicodeEncoding();
            byte[] dataToEncrypt = ByteConverter.GetBytes(WaitHandleContext);
            //使用SHA1进行摘要算法，生成签名
            byte[] encryptedData = rsa.SignData(dataToEncrypt, new SHA1CryptoServiceProvider());
            return System.Convert.ToBase64String(encryptedData);
        }

        /// <summary>
        /// CA公钥验证
        /// </summary>
        /// <param name="WaitHandleContext">原文</param>
        /// <param name="PassWord">密码</param>
        /// <param name="SignedData">签名</param>
        /// <param name="PublicKeyFilePath">公钥</param>
        /// <returns></returns>
        public static bool CASignatureDeformatter(string PublicKeyFilePath, string PassWord, string WaitHandleContext, string SignedData)
        {
            X509Certificate2 objx5092;
            if (string.IsNullOrEmpty(PassWord))
                objx5092 = new X509Certificate2(PublicKeyFilePath);
            else
                objx5092 = new X509Certificate2(PublicKeyFilePath, PassWord);
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            rsa.FromXmlString(objx5092.PublicKey.Key.ToXmlString(false));  //获取公钥
            UnicodeEncoding ByteConverter = new UnicodeEncoding();
            byte[] dataToVerifyBytes = ByteConverter.GetBytes(WaitHandleContext);
            byte[] signedDataBytes = System.Convert.FromBase64String(SignedData);
            return rsa.VerifyData(dataToVerifyBytes, new SHA1CryptoServiceProvider(), signedDataBytes);
        }

        /// <summary>
        /// RSA加密
        /// </summary>
        /// <param name="Data">原文</param>
        /// <param name="PublicKeyString">公钥</param>
        /// <param name="KeyType">密钥类型XML/PEM</param>
        /// <returns></returns>
        public static string RSAEncrypt(string Data,string PublicKeyString,string KeyType)
        {
            byte[] data = Encoding.GetEncoding("UTF-8").GetBytes(Data);
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            switch (KeyType)
            {
                case "XML":
                    rsa.FromXmlString(PublicKeyString);
                    break;
                case "PEM":
                    rsa = RSA_PEM.FromPEM(PublicKeyString);
                    break;
                default:
                    throw new Exception("不支持的密钥类型");
            }
            //加密块最大长度限制，如果加密数据的长度超过 秘钥长度/8-11，会引发长度不正确的异常，所以进行数据的分块加密
            int MaxBlockSize = rsa.KeySize / 8 - 11;
            //正常长度
            if (data.Length <= MaxBlockSize)
            {
                byte[] hashvalueEcy = rsa.Encrypt(data, false); //加密
                return System.Convert.ToBase64String(hashvalueEcy);
            }
            //长度超过正常值
            else
            {
                using (MemoryStream PlaiStream = new MemoryStream(data))
                using (MemoryStream CrypStream = new MemoryStream())
                {
                    Byte[] Buffer = new Byte[MaxBlockSize];
                    int BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize);
                    while (BlockSize > 0)
                    {
                        Byte[] ToEncrypt = new Byte[BlockSize];
                        Array.Copy(Buffer, 0, ToEncrypt, 0, BlockSize);

                        Byte[] Cryptograph = rsa.Encrypt(ToEncrypt, false);
                        CrypStream.Write(Cryptograph, 0, Cryptograph.Length);
                        BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize);
                    }
                    return System.Convert.ToBase64String(CrypStream.ToArray(), Base64FormattingOptions.None);
                }
            }
        }

        /// <summary>
        /// RSA解密
        /// </summary>
        /// <param name="Data">密文</param>
        /// <param name="PrivateKeyString">私钥</param>
        /// <param name="KeyType">密钥类型XML/PEM</param>
        /// <returns></returns>
        public static string RSADecrypt(string Data,string PrivateKeyString, string KeyType)
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
            switch (KeyType)
            {
                case "XML":
                    rsa.FromXmlString(PrivateKeyString);
                    break;
                case "PEM":
                    rsa = RSA_PEM.FromPEM(PrivateKeyString);
                    break;
                default:
                    throw new Exception("不支持的密钥类型");
            }
            int MaxBlockSize = rsa.KeySize / 8;    //解密块最大长度限制
            //正常解密
            if (Data.Length <= MaxBlockSize)
            {
                byte[] hashvalueDcy = rsa.Decrypt(System.Convert.FromBase64String(Data), false);//解密
                return Encoding.GetEncoding("UTF-8").GetString(hashvalueDcy);
            }
            //分段解密
            else
            {
                using (MemoryStream CrypStream = new MemoryStream(System.Convert.FromBase64String(Data)))
                using (MemoryStream PlaiStream = new MemoryStream())
                {
                    Byte[] Buffer = new Byte[MaxBlockSize];
                    int BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize);

                    while (BlockSize > 0)
                    {
                        Byte[] ToDecrypt = new Byte[BlockSize];
                        Array.Copy(Buffer, 0, ToDecrypt, 0, BlockSize);

                        Byte[] Plaintext = rsa.Decrypt(ToDecrypt, false);
                        PlaiStream.Write(Plaintext, 0, Plaintext.Length);
                        BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize);
                    }
                    string output = Encoding.GetEncoding("UTF-8").GetString(PlaiStream.ToArray());
                    return output;
                }
            }
        }

        #region 密钥生成
        /// <summary>
        /// 取得私钥和公钥 XML 格式,返回数组第一个是私钥,第二个是公钥.
        /// </summary>
        /// <param name="size">密钥长度,默认1024,可以为2048</param>
        /// <returns></returns>
        public static string[] CreateXmlKey(int size = 1024)
        {
            //密钥格式要生成pkcs#1格式的  而不是pkcs#8格式的
            RSACryptoServiceProvider sp = new RSACryptoServiceProvider(size);
            string privateKey = sp.ToXmlString(true);//private key
            string publicKey = sp.ToXmlString(false);//public  key
            return new string[] { privateKey, publicKey };
        }

        /// <summary>
        /// 取得私钥和公钥 CspBlob 格式,返回数组第一个是私钥,第二个是公钥.
        /// </summary>
        /// <param name="size"></param>
        /// <returns></returns>
        public static string[] CreateCspBlobKey(int size = 1024)
        {
            //密钥格式要生成pkcs#1格式的  而不是pkcs#8格式的
            RSACryptoServiceProvider sp = new RSACryptoServiceProvider(size);
            string privateKey = System.Convert.ToBase64String(sp.ExportCspBlob(true));//private key
            string publicKey = System.Convert.ToBase64String(sp.ExportCspBlob(false));//public  key 

            return new string[] { privateKey, publicKey };
        }
        /// <summary>
		/// 导出PEM PKCS#1格式密钥对，返回数组第一个是私钥,第二个是公钥.
		/// </summary>
		public static string[] CreateKey_PEM_PKCS1(int size = 1024)
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(size);
            string privateKey = RSA_PEM.ToPEM(rsa, false, false);
            string publicKey = RSA_PEM.ToPEM(rsa, true, false);
            return new string[] { privateKey, publicKey };
        }

        /// <summary>
        /// 导出PEM PKCS#8格式密钥对，返回数组第一个是私钥,第二个是公钥.
        /// </summary>
        public static string[] CreateKey_PEM_PKCS8(int size = 1024, bool convertToPublic = false)
        {
            RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(size);
            string privateKey = RSA_PEM.ToPEM(rsa, false, true);
            string publicKey = RSA_PEM.ToPEM(rsa, true, true);
            return new string[] { privateKey, publicKey };

        }
        #endregion

        #endregion

        #region DES（对称）加密
        /// <summary>
        /// 20170812
        /// ypeng
        /// DES加密
        /// </summary>
        /// <param name="Keys">默认秘钥向量，例子：0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF</param>
        /// <param name="Key">加密密码</param>
        /// <param name="Data">待加密数据</param>
        /// <returns></returns>
        public static string DESEncrypt(byte[] Keys, string Key, string Data)
        {
            byte[] keyBytes = Encoding.UTF8.GetBytes(Key.Substring(0, 8));
            byte[] keyIV = Keys;
            byte[] inputByteArray = Encoding.UTF8.GetBytes(Data);
            DESCryptoServiceProvider provider = new DESCryptoServiceProvider();
            MemoryStream mStream = new MemoryStream();
            CryptoStream cStream = new CryptoStream(mStream, provider.CreateEncryptor(keyBytes, keyIV), CryptoStreamMode.Write);
            cStream.Write(inputByteArray, 0, inputByteArray.Length);
            cStream.FlushFinalBlock();
            return System.Convert.ToBase64String(mStream.ToArray());
        }

        /// <summary>
        /// 20170812
        /// ypeng
        /// DES解密
        /// </summary>
        /// <param name="Keys">默认秘钥向量</param>
        /// <param name="Key">解密密码</param>
        /// <param name="Cryptograph">密文</param>
        /// <returns></returns>
        public static string DESDecrypt(byte[] Keys, string Key, string Cryptograph)
        {
            byte[] keyBytes = Encoding.UTF8.GetBytes(Key.Substring(0, 8));
            byte[] keyIV = Keys;
            byte[] inputByteArray = System.Convert.FromBase64String(Cryptograph);
            DESCryptoServiceProvider provider = new DESCryptoServiceProvider();
            MemoryStream mStream = new MemoryStream();
            CryptoStream cStream = new CryptoStream(mStream, provider.CreateDecryptor(keyBytes, keyIV), CryptoStreamMode.Write);
            cStream.Write(inputByteArray, 0, inputByteArray.Length);
            cStream.FlushFinalBlock();
            return Encoding.UTF8.GetString(mStream.ToArray());
        }
        #endregion

        #region MD5加密
        /// <summary>   
        /// MD5加密   
        /// </summary>   
        /// <param name="str"></param>   
        /// <returns></returns>   
        public static string MD5Encrypt(string str)
        {
            MD5 md5Hasher = MD5.Create();
            byte[] data = md5Hasher.ComputeHash(Encoding.GetEncoding("UTF-8").GetBytes(str));
            StringBuilder sBuilder = new StringBuilder();
            for (int i = 0; i < data.Length; i++)
            {
                sBuilder.Append(data[i].ToString("x2"));
            }
            return sBuilder.ToString();

        }
        #endregion

        #region Base64
        public static string GetFromBase64String(string Data)
        {
            byte[] BaseStr = System.Convert.FromBase64String(Data);
            string result = System.Text.Encoding.Default.GetString(BaseStr);
            return result;
        }
        public static string GetBase64String(string Data)
        {
            byte[] BaseByte = System.Text.Encoding.Default.GetBytes(Data);
            string result = System.Convert.ToBase64String(BaseByte);
            return result;
        }
        #endregion
    }
}
